#include <func.h>

#define BACKLOG 10
#define MAXLINE 1024

int tcp_listen(const char* port)
{
    // 服务器监听 port 端口
    struct addrinfo hints;
    bzero(&hints, sizeof(hints));
    hints.ai_flags = AI_PASSIVE;    // 设置通配符地址
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;    // TCP

    struct addrinfo *result, *p;
    if (getaddrinfo(NULL, port, &hints, &result) == -1) {
        return -1;
    }

    // 遍历地址信息，绑定端口
    int sockfd = -1;
    for (p = result; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
            continue;
        }
        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            continue;
        }
        if (listen(sockfd, BACKLOG) == -1) {
            close(sockfd);
            continue;
        }
        // socket, bind, listen 都成功了
        break;
    }  
    // 释放地址信息
    freeaddrinfo(result);
    return sockfd;
}

void* getipaddr(struct sockaddr* addr) 
{
    if (addr->sa_family == AF_INET) {
        return &((struct sockaddr_in*)addr)->sin_addr;
    }
    return &((struct sockaddr_in6*)addr)->sin6_addr;
}

void do_echo(int connfd) 
{
    char buf[MAXLINE];
    int n;
    while ((n = recv(connfd, buf, MAXLINE, 0)) > 0) {
        send(connfd, buf, n, 0);
    }

    if (n == 0) {
        printf("INFO: client shutdown\n");
    } else {
        error(1, errno, "recv()");  // 子进程终止
    }
}

void chld_handler(int sig) 
{
    // 信号是异步
    int save = errno;
    while (waitpid(-1, NULL, WNOHANG) > 0)
        ;
    errno = save;
}

// 阻塞I/O + 多进程
int main(int argc, char* argv[])
{
    // 注册信号处理函数
    signal(SIGCHLD, chld_handler);

    char port[] = "9527";
    int listenfd = tcp_listen(port);
    if (listenfd == -1) {
        error(1, errno, "tcp_listen()");
    }
    printf("INFO: listen %s success\n", port);

    for(;;) {
        struct sockaddr_storage client_addr; 
        socklen_t addrlen = sizeof(client_addr);

        int connfd = accept(listenfd, (struct sockaddr*)&client_addr, &addrlen);
        if (connfd == -1) {
            error(0, errno, "accept()");
            continue;
        }

        char ipstr[50];
        inet_ntop(client_addr.ss_family, getipaddr(&client_addr), ipstr, 50);
        printf("INFO: receive connection from %s\n", ipstr);

        // fork子进程，让子进程和客户端通信，父进程继续等待连接请求
        pid_t pid = fork();
        switch(pid) {
        case -1:
            error(0, errno, "fork()");
            break;
        case 0:
            // 子进程
            close(listenfd);    // 关闭监听文件描述符
            do_echo(connfd);    // 子进程和客户端通信
            close(connfd);      // 子进程和客户端通信完毕 
            exit(0);            // 子进程退出
        default:
            // 父进程
            close(connfd);
            break;
        }
    }
    return 0;
}

